
/* Generated by Interface Builder */
/* [yeah, right, the Interface Builder generated this.  -scott] */

// My stuff.
#import "ClockView.h"
#import "Animator.h"

// appkit stuff.
#import <appkit/nextstd.h>			// MAX()
#import <appkit/Matrix.h>			// For getting the clockType.

// unix stuff.
#import <math.h>				// cos, sin, M_PI

// dps stuff.
#import <dpsclient/wraps.h>			// PS*().

@implementation ClockView

extern long time( long *);

+ newFrame:(NXRect *)r
{
  self = [super newFrame:r];
  [self setClockType:BEZIER];
  [self setDialSize:.05];
  return self;
}

- free
{
  [animator stop:self];
  [animator setTarget:nil];
  return [super free];
}

- takeDialSizeFrom:sender	// set the dial size via slider.
{
  return [self setDialSize:[sender floatValue]];
}
- takeClockTypeFrom:sender	// set the clock type via matrix.
{
  return [self setClockType:[[sender selectedCell] tag]];
}

- setDialSize:(float)size	// Set programatically.
{
  dialSize=size;
  return [self display];
}
- setClockType:(int)type
{
  clockType=type;
  return [self display];
}

-(int)clockType			// return the data.
{
  return clockType;
}
-(float)dialSize
{
  return dialSize;
}

// Draws the dials in the currently focussed region, etc.  Makes little or
// no sense if not used to draw in this view.  Also, won't work nicely for non-
// square views.
- drawDials
{
  float half=bounds.size.height/2.0;
  float secang=sec*6*M_PI/180.0;
  float minang=min*6*M_PI/180.0;
  float hourang=((hour)%12)*30*M_PI/180.0+minang/12;

// define the size multiplier for each hand.
#define HOUR (.4*half)
#define MINUTE (.75*half)
#define SECOND (.85*half)

  PSsetlinewidth( dialSize*half);
  PSsetlinecap( 1);			// Rounded ends.
  PSsetlinejoin( 1);			// Rounded joins.
  PStranslate( half, half);
  PSmoveto( sin( minang)*MINUTE, cos( minang)*MINUTE);
  switch( clockType)
    {
      case NORMAL :			// "Normal" clock.
	PSlineto( 0, 0);
	PSlineto( sin( secang)*SECOND, cos( secang)*SECOND);
	PSlineto( 0, 0);
	PSlineto( sin( hourang)*HOUR, cos( hourang)*HOUR);
        break;
      case LINES :
	PSlineto( sin( secang)*SECOND, cos( secang)*SECOND);
	PSlineto( sin( hourang)*HOUR, cos( hourang)*HOUR);
        break;
      case CURVES :
	PScurveto( 0, 0, 0, 0,
		   sin( secang)*SECOND, cos( secang)*SECOND);
	PScurveto( 0, 0, 0, 0,
		   sin( hourang)*HOUR, cos( hourang)*HOUR);
	PScurveto( 0, 0, 0, 0,
		   sin( minang)*MINUTE, cos( minang)*MINUTE);
        break;
      case BEZIER :
	PScurveto( sin( secang)*SECOND, cos( secang)*SECOND,
	     0, 0, sin( hourang)*HOUR, cos( hourang)*HOUR);
        break;
    }
  PSstroke();
  PStranslate( -half, -half);
  return self;
}

- setAnimator:anObject
{
  if( animator==anObject)			// nip loops in the bud.
    return self;
  animator=anObject;
  [animator setTarget:self];
  [animator setTiming:1.0];
  [animator setThreshold:NX_MODALRESPTHRESHOLD];	// this makes it run smoother. why?
  return self;
}

- animate:sender
{
  long clock;
  struct tm *t;

  [self lockFocus];
  
  PSsetgray( NX_LTGRAY);		// Erase the old time.
  [self drawDials];
  
  time( &clock);			// Snarf the new time.
  t=localtime( &clock);
  hour=t->tm_hour;
  min=t->tm_min;
  sec=t->tm_sec;
  
  PSsetgray( NX_BLACK);			// Draw the new time.
  [self drawDials];
  
  [self unlockFocus];			// Make it be.
  [[self window] flushWindow];
  NXPing();
  return self;
}

- animator
{
  return animator;
}

- drawSelf:(NXRect *)r :(int)count
{
  long clock;
  struct tm *t;
  int i;
  float half=bounds.size.height/2.0;
#define TICKS (.95*half)

  time( &clock);			// Snarf the time.
  t=localtime( &clock);
  hour=t->tm_hour;
  min=t->tm_min;
  sec=t->tm_sec;
  
  PSsetgray( NX_LTGRAY);		// Clear the background.
  NXRectFill( &bounds);

  PSsetgray( NX_BLACK);			// Draw the dials.
  [self drawDials];

  
  PStranslate( half, half);
  // Then, draw the tick marks (assume linewidth/cap set by drawDials).
  PSnewpath();
  for( i=0; i<12; i++, PSrotate( 30))
    {
      PSmoveto( TICKS, 0);
      PSlineto( half, 0);
    }
  PSstroke();
  
  [[self window] flushWindow];
  NXPing();
  return self;
}

// Here is a useful function - cut this out for your programs.  This is a Window
// delegate function which keeps the window from resizing to a non-square
// size.  It's not what I'd like, though - the resize bar still has that
// section for resizing vertically only.  When that occurs, you do not get
// to do anything.  I tried _making_ it resize, but that did not work.  So,
// this is what you get.
- windowWillResize:sender toSize:(NXSize *)f
{
  NXRect curFrame;
  [sender getFrame:&curFrame];
  if( curFrame.size.width==f->width || curFrame.size.height==f->height)
    {
      *f=curFrame.size;
      return self;
    }
  f->width-=curFrame.size.width;
  f->height-=curFrame.size.height;
  f->width=f->height=MAX( f->width, f->height);
  f->width+=curFrame.size.width;
  f->height+=curFrame.size.height;
  return self;
}

@end
